﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Collections.Specialized;

namespace MsiExecEx
{
    class Program
    {
        private static int Timeout = Int16.MaxValue;

        static void Main(string[] args)
        {
            StringBuilder commandLine = new StringBuilder();

            foreach (var a in args)
            {
                commandLine.Append(a);
                commandLine.Append(" ");
            }

            ExecuteTool("msiexec", commandLine.ToString());
        }

        protected static int ExecuteTool(string pathToTool, string commandLineCommands)
        {
            Process proc = null;
            int exitCode = 0;

            try
            {
                // Build up the command line that will be spawned.
                string commandLine = commandLineCommands;

                ProcessStartInfo startInfo = new ProcessStartInfo(pathToTool, commandLine);
                startInfo.CreateNoWindow = true;
                startInfo.UseShellExecute = false;
                startInfo.RedirectStandardError = true;
                startInfo.RedirectStandardOutput = true;

                // Some applications such as xcopy.exe fail without error if there's no stdin stream.
                // See bug 323655.
                startInfo.RedirectStandardInput = true;


                // PPD@31115: TODO: Need to set startInfo.WorkingDirectory also
                // How to get it? Here are the ways I can think of:
                // 1. Each task can provide it like the ToolName property
                //    A task can get this information from project system through the host object
                // 2. We have a callback to the build Engine implemented today.
                //    IBuildEngine should provide "reflection" functionality on the current project file
                //    Meaning - a task can completely read the project file using this "reflection" functionality
                //    We can then use this functionality to read the MSBuildProjectDirectory property from the project file 

                // Start the process
                proc = Process.Start(startInfo);

                // Wait for the process to complete or until the specified timeout
                proc.WaitForExit(Timeout);

                bool loggingThreadsStopped = false;

                // Did we have timout specified? Kill the process if not finished yet
                if ((Timeout != int.MaxValue) && !proc.HasExited)
                {
                    // abort the logging threads
                    loggingThreadsStopped = true;
                    proc.Kill();
                }

                // Hack necessitated by VSWhidbey 118690.
                // Process.WaitForExit() may return prematurely. We need to check to be sure.
                while (!proc.HasExited)
                {
                    System.Threading.Thread.Sleep(50);
                }

                if (!loggingThreadsStopped)
                {
                }

            }
            finally
            {
                // get the exit code and release the process handle
                if (proc != null)
                {
                    exitCode = proc.ExitCode;
                    proc.Close();
                    proc = null;
                }

                // If the tool exited cleanly, but logged errors then assign a failing exit code (-1)
                if (exitCode == 0)
                {
                    exitCode = -1;
                }
            }

            return exitCode;
        }
    }
}
